package com.iacrqq.sedaf.stage;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * 
 * @author sihai
 *
 */
public class StageRuntimeStatisticsCollector {
	
	public static int DEFAULT_HISTORY_COUNT = 1024;
	private long startTime;
	private long endTime = -1;
	private volatile long ts;
	private volatile long nops;
	private volatile long fulls;
	private volatile long currentThreadCount;
	private volatile long idleThreadCount;
	private List<Long> handlerConsumeTime;
	private Object _list_lock_;
	private int history = DEFAULT_HISTORY_COUNT;
	private long _last_start;
	
	
	public StageRuntimeStatisticsCollector(int history)
	{
		this.history = history;
		_list_lock_ = new Object();
		handlerConsumeTime = new LinkedList<Long>();
	}
	
	public void start()
	{
		this.startTime = System.currentTimeMillis();
	}
	
	public void end()
	{
		this.endTime = System.currentTimeMillis();
	}
	
	public void completed()
	{
		ts++;
	}
	
	public void nop()
	{
		nops++;
	}
	
	public void full()
	{
		fulls++;
	}

	public void setCurrentThreadCount(long currentThreadCount)
	{
		this.currentThreadCount = currentThreadCount;
	}

	public void setIdleThreadCount(long idleThreadCount)
	{
		this.idleThreadCount = idleThreadCount;
	}
	
	public void startHandle()
	{
		_last_start = System.currentTimeMillis();
	}
	
	public void endHandle()
	{
		synchronized(_list_lock_)
		{
			if(handlerConsumeTime.size() == history)
			{
				_clear_old_half_();
			}
			
			handlerConsumeTime.add(System.currentTimeMillis() - _last_start);
		}
	}
	
	private void _clear_old_half_()
	{
		//List oldHalf = handlerConsumeTime.subList(0, history / 2 - 1);
		List<Long> newHalf = new LinkedList<Long>();
		newHalf.addAll(handlerConsumeTime.subList(history / 2, history - 1));
		
		handlerConsumeTime.clear();
		handlerConsumeTime.addAll(newHalf);
		
		newHalf.clear();
	}
	
	private double getTPS()
	{
		if(endTime == -1)
		{
			return 1000.00D * ts / (System.currentTimeMillis() - startTime);
		}
		else
		{
			return 1000.00D * ts / (endTime - startTime);
		}
	}
	
	private double getNOPR()
	{
		if(endTime == -1)
		{
			return 1000.00D * nops / (System.currentTimeMillis() - startTime);
		}
		else
		{
			return 1000.00D * nops / (endTime - startTime);
		}
	}
	
	private double getFR()
	{
		if(endTime == -1)
		{
			return 1000.00D * fulls / (System.currentTimeMillis() - startTime);
		}
		else
		{
			return 1000.00D * fulls / (endTime - startTime);
		}
	}
	
	public StageRuntimeStatistics snapshot(Stage stage)
	{
		StageRuntimeStatistics snapshot = new StageRuntimeStatistics();
		
		snapshot.setTs(ts);
		snapshot.setNops(nops);
		snapshot.setFulls(fulls);
		snapshot.setFr(getFR());
		snapshot.setNopr(getNOPR());
		snapshot.setTps(getTPS());
		snapshot.setCurrentThreadCount(stage.getCurrentThreadCount());
		snapshot.setIdleThreadCount(stage.getIdleTreadCount());
		
		List<Long> list = new LinkedList<Long>();
		synchronized(_list_lock_)
		{
			list.addAll(handlerConsumeTime);
		}
		snapshot.setHandlerConsumeTime(list);
		
		return snapshot;
	}
	
	@Override
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		
		sb.append("StageRuntimeStatisticsCollector{startTime:");
		sb.append(new Date(startTime));
		sb.append(",endTime:");
		sb.append(new Date(endTime));
		sb.append(",ts:");
		sb.append(ts);
		sb.append(",nops:");
		sb.append(nops);
		sb.append(",fulls:");
		sb.append(fulls);
		sb.append(",tps:");
		sb.append(getTPS());
		sb.append(",nopr:");
		sb.append(getNOPR());
		sb.append(",fr:");
		sb.append(getFR());
		sb.append(",currentThreadCount:");
		sb.append(currentThreadCount);
		sb.append(",idleThreadCount:");
		sb.append(idleThreadCount);
		sb.append("}");
		return sb.toString();
	}
}
